package com.rxyb.security.config;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rxyb.security.db.bean.SysRole;
import com.rxyb.security.db.bean.SysUser;
import com.rxyb.security.db.bean.SysUserRole;
import com.rxyb.security.db.bean.SysUsercustom;
import com.rxyb.security.db.dao.SysRoleMapper;
import com.rxyb.security.db.dao.SysUserMapper;
import com.rxyb.security.db.dao.SysUserRoleMapper;
import com.rxyb.security.handler.LoginSuccessHandler;
import com.rxyb.security.handler.AuthLimitHandler;
import com.rxyb.security.handler.UrlRoleAuthHandler;
import com.rxyb.security.handler.UrlRolesFilterHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.access.vote.UnanimousBased;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

/**
 * @author YJH
 * @version 1.0
 * @description Web 认证的怕配置
 * @date 2020/5/15  15:51
 */
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;


    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // 关闭csrf
        http.csrf().disable();
        // 默认登录页面
        http.formLogin().loginPage("/login").permitAll();

        // 配置登录成功后默认页面
        http.formLogin().successHandler(new LoginSuccessHandler());
        // 访问权限不足页面跳转
        http.exceptionHandling().accessDeniedHandler(new AuthLimitHandler());
        // 登出登出
        http.logout().permitAll();
        // 配置登录后默认页面
        // 授权配置 调用authorizeRequests()的意思是指通过authorizeRequests()方法来开始请求权限配置，
        // anyRequest().fullyAuthenticated()是对http所有的请求必须通过授权认证才可以访问。除了fullyAuthenticated方法，
        // 还可以使用authenticated方法
        http.authorizeRequests()
                /*所有的静态资源可以访问*/
                .antMatchers("/js/**", "/css/**", "images/**").permitAll()
                /*所有以 ad 开头的资源可以访问*/
                .antMatchers("/ad/**").permitAll()
                // 动态得url 加载
                .withObjectPostProcessor(new DefaultObjectPostProcessor())
                // url 决策
                .accessDecisionManager(accessDecisionManager())
                .anyRequest().fullyAuthenticated();
    }

    @Override
    protected UserDetailsService userDetailsService() {
        return username -> {
            if (null == username || username.trim().length() <= 0) {
                throw new UsernameNotFoundException("用户名为空");
            }
//            // 查询用户信息
            QueryWrapper<SysUser> sysUserQueryWrapper = new QueryWrapper<>();
            sysUserQueryWrapper.lambda().eq(SysUser::getUsername, username);
            SysUser sysUser1 = sysUserMapper.selectOne(sysUserQueryWrapper);
            // 查询权限信息
            QueryWrapper<SysUserRole> sysUserRoleQueryWrapper = new QueryWrapper<>();
            sysUserRoleQueryWrapper.lambda().eq(SysUserRole::getSysUserId, sysUser1.getId());
            List<SysUserRole> sysUserRoles = sysUserRoleMapper.selectList(sysUserRoleQueryWrapper);
            //  查询权限
            QueryWrapper<SysRole> sysRoleQueryWrapper = new QueryWrapper<>();
            sysRoleQueryWrapper.lambda().in(SysRole::getId, str(sysUserRoles));
            List<SysRole> sysRoles = sysRoleMapper.selectList(sysRoleQueryWrapper);
            SysUsercustom sysUser = new SysUsercustom();
            sysUser.setId(sysUser1.getId());
            sysUser.setUsername(sysUser1.getUsername());
            sysUser.setPassword(sysUser1.getPassword());
            sysUser.setSysRoleList(sysRoles);
            if (null != sysUser) {

                return sysUser;
            }
            throw new UsernameNotFoundException("用户信息不存在");
        };
    }

    private List<Long> str(List<SysUserRole> roles) {
        List<Long> list = new ArrayList<>();
        roles.forEach(e -> {
            list.add(e.getSysRoleId());
        });
        return list;
    }



    /**
     * AffirmativeBased – 任何一个AccessDecisionVoter返回同意则允许访问
     * ConsensusBased – 同意投票多于拒绝投票（忽略弃权回答）则允许访问
     * UnanimousBased – 每个投票者选择弃权或同意则允许访问
     *
     * 决策管理
     */
    private AccessDecisionManager accessDecisionManager() {
        List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<>();
        decisionVoters.add(new WebExpressionVoter());
        decisionVoters.add(new AuthenticatedVoter());
        decisionVoters.add(new RoleVoter());
        /* 路由权限管理 */
        decisionVoters.add(new UrlRoleAuthHandler());
        return new UnanimousBased(decisionVoters);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService()).passwordEncoder(new BCryptPasswordEncoder());
    }
//
//    @Bean
//    public JdbcUserDetailsManager jdbcUserDetailsManager(DataSource dataSource) {
//        JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager();
//        jdbcUserDetailsManager.setDataSource(dataSource);
//        return jdbcUserDetailsManager;
//    }


    @Autowired
    private UrlRolesFilterHandler urlRolesFilterHandler;

    class DefaultObjectPostProcessor implements ObjectPostProcessor<FilterSecurityInterceptor> {
        @Override
        public <O extends FilterSecurityInterceptor> O postProcess(O object) {
            object.setSecurityMetadataSource(urlRolesFilterHandler);
            return object;
        }
    }
}
